package org.rkuk.kasutil.test.framework 
{
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	import org.rkuk.kasutil.test.event.TestMethodEvent;
	import org.rkuk.kasutil.test.framework.TestMethod;
	import org.rkuk.kasutil.test.system.handler.AnsyEventHandler;
	import org.rkuk.kasutil.test.system.TestContext;
	import org.rkuk.kasutil.test.system.TestThread;
	/**
	 * ...
	 * @author rkuk
	 */
	public class AnsyTestMethod extends TestMethod 
	{
		private var ansyHandlerInfo_:Dictionary;
		private var ansyEventHandler_:Array;
		private var timeoutHandler_:Array;
		protected var isSuccess_:Boolean;
		
		public function AnsyTestMethod(methodName:String, isStatic:Boolean) 
		{
			super(methodName, isStatic);
			isSuccess_ = true;
		}
		
		public function addAnsyHandlerInfo(target:String, eventType:String, eventMethod:TestMethod, timeout:int, timeoutMethod:TestMethod):void
		{
			if (ansyHandlerInfo_ == null)
				ansyHandlerInfo_ = new Dictionary();
				
			var hashKey:String = target + "::" + eventType;
			if (!(hashKey in ansyHandlerInfo_))
				ansyHandlerInfo_[hashKey] = new Array();
			var registeredHandler:Array = ansyHandlerInfo_[hashKey] as Array;
			
			
			if (eventMethod == this)
				eventMethod = null;
			if (timeoutMethod == this)
				timeoutMethod = null;
				
			if (eventMethod != null && timeoutMethod != null)
			{
				if (ansyHandlerInfo_ == null)
				{
					ansyHandlerInfo_ = new Array();
					ansyEventHandler_ = new Array();
					timeoutHandler_ = new Array();
				}
				
				eventMethod.parent = timeoutMethod.parent = parent;
				
				var info:AnsyHandlerInfo = new AnsyHandlerInfo();
				info.target = target || "this";
				info.eventType = eventType;
				info.timeout = timeout;
				info.eventHandler = eventMethod;
				info.timeoutHandler = timeoutMethod;
				
				ansyHandlerInfo_.push(info);
				ansyEventHandler_.push(eventMethod);
				timeoutHandler_.push(timeoutMethod);
			}
		}
		
		override public function execute(testContext:TestContext):int 
		{
			var invoker:Object = getInvoker(testContext);
			registerAnsyHandler(invoker, testContext);
			executionTime_ = getTimer();
			//TODO:consider unregister ansyHandler when failed in TestThread
			invoker[methodName_].apply(invoker, params_);
			
			return getTimer() - executionTime_;
		}
		
		/**
		 * 从addAnsyHandlerInfo方法添加的AnsyHandlerInfo中，读取信息并完成异步事件的征听器添加
		 * @param	invoker
		 * @param	testThread
		 */
		private function registerAnsyHandler(invoker:Object, testContext:TestContext):void 
		{
			for (var i:int = 0; i < ansyHandlerInfo_.length; i++ )
			{
				var info:AnsyHandlerInfo = ansyHandlerInfo_.[i] as AnsyHandlerInfo;
				
				//TODO:考虑如果没有eventHandler或timeoutHandler时，如何让AnsyTestMethod知道事件执行情况
				if (info.eventHandler != null)//为了防止在TestMethodGroup中重复征听
					info.eventHandler.addEventListener(TestMethodEvent.EXECUTE, onAnsyEvent);
				if (info.timeoutHandler != null)
					info.timeoutHandler.addEventListener(TestMethodEvent.EXECUTE, onTimeout);
					
				//TODO:考虑target == null时的处理
				var target:EventDispatcher = getAnsyEventTarget(invoker, info.target);
				testContext.pushHandlerToStack(new AnsyEventHandler(target,))
				testThread.registerAnsyHandler(target, info.eventType, info.eventHandler, info.timeout, info.timeoutHandler);
			}
		}
		
		/**
		 * 在当前TestMethod的测试方法真正执行前，通过registerAnsyHandler先注册Ansy事件的征听器，发出事件的对象从这里获得
		 * @param	invoker
		 * @param	target
		 * @return
		 * @see		registerAnsyHandler()
		 */
		protected function getAnsyEventTarget(invoker:Object, target:String):EventDispatcher;
		{
			return ((target == "this" || !invoker.hasOwnProperty(target))?invoker:invoker[target]) as EventDispatcher;
		}
		
		private function onAnsyEvent(e:TestMethodEvent):void 
		{
			var index:int = ansyEventHandler_.indexOf(e.target);
			removeAnsyHandler(index);
			tryToNotifyExecute(e.isSuccess, e.message);
		}
		
		private function onTimeout(e:TestMethodEvent):void 
		{
			var index:int = timeoutHandler_.indexOf(e.target);
			removeAnsyHandler(index);
			tryToNotifyExecute(e.isSuccess, e.message);
		}
		
		private function removeAnsyHandler(index:int):void 
		{
			TestMethod(ansyEventHandler_.splice(index, 1)[0]).removeEventListener(TestMethodEvent.EXECUTE, onAnsyEvent);
			TestMethod(timeoutHandler_.splice(index, 1)[0]).removeEventListener(TestMethodEvent.EXECUTE, onTimeout);
		}
		
		private function tryToNotifyExecute(isSuccess:Boolean, message:String):void 
		{
			isSuccess_ = isSuccess_ && isSuccess;
			//TODO:gather all the message, not only the last
			if (ansyEventHandler_.length <= 0)
				notifiyExecute(isSuccess, message);
		}
		
		override protected function set parent(value:TestMethodGroup):void 
		{
			super.parent = value;
			if (ansyEventHandler_ != null)
			{
				for (var i:int = 0; i < ansyEventHandler_.length; i++)
				{
					var method:TestMethod;
					method = ansyEventHandler_[i] as TestMethod;
					method.parent = value;
					
					method = timeoutHandler_[i] as TestMethod;
					method.parent = value;
				}
			}
		}
		
		override protected function clone():TestMethod 
		{
			var cloneMethod:AnsyTestMethod = new AnsyTestMethod(methodName_, isStatic_);
			
			if (ansyHandlerInfo_ != null)
			{
				for (var i:int = 0; i < ansyHandlerInfo_.length; i++ )
				{
					var info:AnsyHandlerInfo = ansyHandlerInfo_[i] as AnsyHandlerInfo;
					//TODO:what about if the info.eventHandler or info.timeoutHandler is null
					cloneMethod.addAnsyHandlerInfo(info.target, info.eventType, info.eventHandler.clone(), info.timeout, info.timeoutHandler.clone());
				}
			}
			
			return cloneMethod;
		}
	}
}
class AnsyHandlerInfo
{
	public var target:String;
	public var eventType:String;
	public var eventMethod:TestMethod
	public var timeout:int;
	public var timeoutMethod:TestMethod
}
